
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using Swaf.Logging;

namespace Swaf.Container
{
	/// <summary>
	/// Summary description for ChangeJournalExpireNode.
	/// </summary>
	public class ChangeJournalExpireNode : ExpireNodeBase
	{
		private string m_fileName="";
		private string m_filePath="";
		private IList m_monitors = new ArrayList();
		protected bool m_isTimeExpireAlso=false;
		protected long m_timeoutTime;
		protected long m_timeToWait;
		protected bool m_sliding;

		public ChangeJournalExpireNode(Match mr, params object[] extraInfo) : this(extraInfo[0])
		{
			//Check for included time expire
			
			if(mr.Groups.Count > 2)
			{
				//We at least have a time expire here.  Let's see if there is also a file.
				if (mr.Groups[1].Value.Equals("in") || mr.Groups[1].Value.Equals("after"))
				{
					//We know the first entry is for time, find the second entry
					initTimeExpire(mr.Groups[1].Value,mr.Groups[2].Value,mr.Groups[3].Value);
					if(mr.Groups.Count == 6)
					{
						//We know we have a file at the end.
						initFileMonitors(mr.Groups[5].Value);
					}
				}
				else
				{
					//We know the first item is a file.
					initFileMonitors(mr.Groups[1].Value);
				}
			}
			else
			{
				//This is only a file monitor.
				initFileMonitors(mr.Groups[1].Value);
			}
		}
		public ChangeJournalExpireNode(string fileNames, object value) : this(value)
		{
			initFileMonitors(fileNames);
		}

		public override object get(string nameAddress, params object[] infoArgs)
		{
			if (m_sliding)
				m_timeoutTime = DateTime.Now.Ticks + m_timeToWait;
			return base.get(nameAddress, infoArgs);
		}

		public override void put(string nameAddress, object val)
		{
			if (m_sliding)
				m_timeoutTime = DateTime.Now.Ticks + m_timeToWait;
			base.put(nameAddress, val);
		}

		private ChangeJournalExpireNode(object value)
		{
			m_value = value;
		}

		private void initTimeExpire(string type, string timeLen, string timeType)
		{
			m_isTimeExpireAlso=true;
			double tLength = Double.Parse(timeLen);

			m_sliding = (String.Compare(type, "after") == 0)?true:false;
			m_timeToWait = calcTime(tLength, timeType);
			m_timeoutTime = DateTime.Now.Ticks + m_timeToWait;
			
		}
		protected long calcTime(double timeLength, string timeType)
		{
			long amountOfTime = 0;
			int oneSecond = 1000 * 10000; //.NET DateTime.Ticks uses 100 nanoseconds per "tick";
			IApplication app = Application.currentApp;
			if(app.resMgr.matches("{sec}", timeType).Success)
				amountOfTime = (long)(timeLength * oneSecond);
			else if(app.resMgr.matches("{minute}", timeType).Success)
				amountOfTime = (long)(timeLength * 60 * oneSecond);
			else if(app.resMgr.matches("{hour}", timeType).Success)
				amountOfTime = (long)(timeLength * 60 * 60 * oneSecond);
			else if(app.resMgr.matches("{day}", timeType).Success)
				amountOfTime = (long)(timeLength * 24 * 60 * 60 * oneSecond);

			return amountOfTime;
		}


		private void initFileMonitors(string fileNames)
		{
			string[] files;
			if (fileNames.IndexOf(',') > -1)
				files=fileNames.Split(',');
			else
			{
				files = new string[1];
				files[0] = fileNames;
			}
			foreach(string fileName in files)
			{
				string baseFileName="";
				string filePath="";
				try
				{
					FileInfo info = new FileInfo(fileName);
					baseFileName=info.Name;
					filePath=info.Directory.FullName;
					FileSystemWatcher monitor = new FileSystemWatcher(filePath,baseFileName);
					monitor.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite 
						| NotifyFilters.FileName | NotifyFilters.DirectoryName;

					// Add event handlers.
					monitor.Changed +=new FileSystemEventHandler(monitor_Changed);
					monitor.Created += new FileSystemEventHandler(monitor_Changed);
					monitor.Deleted += new FileSystemEventHandler(monitor_Changed);
					monitor.Renamed +=new RenamedEventHandler(monitor_Renamed);

					//Begin monitoring.
					monitor.EnableRaisingEvents = true;
					m_monitors.Add(monitor);

				}
				catch(Exception e)
				{
					Application.currentApp.Trace.write(TraceLevels.Verbose,"Error creating change journal monitor for folder {0}.\n{1}",filePath,e.ToString());
				}
			}
			
		}
		#region IExpireNode Members
		public override void check()
		{
			if(m_isTimeExpireAlso && DateTime.Now.Ticks > m_timeoutTime)
				clear();
		}

		#endregion

		private void monitor_Changed(object sender, FileSystemEventArgs e)
		{
			//Expire the node on any change.
			Application.currentApp.Trace.write(TraceLevels.Verbose,"Expired file {0}",((FileSystemWatcher) sender).Path);
			clear();
		}

		private void monitor_Renamed(object sender, RenamedEventArgs e)
		{
			//Expire the node on any change.
			Application.currentApp.Trace.write(TraceLevels.Verbose,"Expired file {0}",((FileSystemWatcher) sender).Path);
			clear();
		}
	}
}
